#include <xscript/xitemboolean.h>
#include <xscript/xitemint32.h>
#include <xscript/xitemfloat.h>
#include <xscript/xscriptexecutor.h>
#include <astree/xabstractsyntaxtreecompare.h>

XAbstractSyntaxTreeCompare::XAbstractSyntaxTreeCompare(XTokenPtr inputToken):
    XAbstractSyntaxTree(inputToken) {
    result = std::make_shared<XItemBoolean>(false);
}

XAbstractSyntaxTreeGreate::XAbstractSyntaxTreeGreate(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L">");
}

bool XAbstractSyntaxTreeGreate::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    if (a->asInt32()) {
        bool flag = greate<int32_t>(a->asInt32()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    } else if (a->asFloat()) {
        bool flag = greate<float>(a->asFloat()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    }
    XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
    return false;
}

XAbstractSyntaxTreeLess::XAbstractSyntaxTreeLess(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"<");
}

bool XAbstractSyntaxTreeLess::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    if (a->asInt32()) {
        bool flag = less<int32_t>(a->asInt32()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    } else if (a->asFloat()) {
        bool flag = less<float>(a->asFloat()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    }
    XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
    return false;
}

XAbstractSyntaxTreeEqual::XAbstractSyntaxTreeEqual(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"equal");
}

bool XAbstractSyntaxTreeEqual::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    if (a->asInt32()) {
        bool flag = equal<int32_t>(a->asInt32()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    } else if (a->asFloat()) {
        bool flag = equal<float>(a->asFloat()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    }
    XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
    return false;
}

XAbstractSyntaxTreeGreateOrEqual::XAbstractSyntaxTreeGreateOrEqual(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L">=");
}

bool XAbstractSyntaxTreeGreateOrEqual::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    if (a->asInt32()) {
        bool flag = greate<int32_t>(a->asInt32()->getValue(), b) || equal<int32_t>(a->asInt32()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    } else if (a->asFloat()) {
        bool flag = greate<float>(a->asFloat()->getValue(), b) || equal<float>(a->asFloat()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    }
    XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
    return false;
}

XAbstractSyntaxTreeLessOrEqual::XAbstractSyntaxTreeLessOrEqual(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"<=");
}

bool XAbstractSyntaxTreeLessOrEqual::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->isNumber()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    if (a->asInt32()) {
        bool flag = less<int32_t>(a->asInt32()->getValue(), b) || equal<int32_t>(a->asInt32()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    } else if (a->asFloat()) {
        bool flag = less<float>(a->asFloat()->getValue(), b) || equal<float>(a->asFloat()->getValue(), b);
        result->asBoolean()->setValue(flag);
        return true;
    }
    XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
    return false;
}

XAbstractSyntaxTreeAnd::XAbstractSyntaxTreeAnd(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"and");
}

bool XAbstractSyntaxTreeAnd::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->asBoolean()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->asBoolean()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    result->asBoolean()->setValue(a->asBoolean()->getValue() && b->asBoolean()->getValue());
    return true;
}

XAbstractSyntaxTreeOr::XAbstractSyntaxTreeOr(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"or");
}

bool XAbstractSyntaxTreeOr::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 2) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    ok = getChildAt(1)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();
    auto b = getChildAt(1)->getResult();

    if (!a->asBoolean()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    if (!b->asBoolean()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(1)->getToken()->toString());
        return false;
    }

    result->asBoolean()->setValue(a->asBoolean()->getValue() || b->asBoolean()->getValue());
    return true;
}

XAbstractSyntaxTreeNot::XAbstractSyntaxTreeNot(XTokenPtr inputToken):
    XAbstractSyntaxTreeCompare(inputToken) {
    setName(L"not");
}

bool XAbstractSyntaxTreeNot::invoke(std::shared_ptr<XVariableTable> table) {
    if (getChildCount() != 1) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, token->toString());
        return false;
    }

    auto ok = getChildAt(0)->invoke(table);
    if (!ok)
        return false;

    auto a = getChildAt(0)->getResult();

    if (!a->asBoolean()) {
        XScriptExecutor::getInstance()->setError(XError::XError_ArgumentCountError, getChildAt(0)->getToken()->toString());
        return false;
    }

    result->asBoolean()->setValue(!a->asBoolean()->getValue());
    return true;
}
